package cn.bonoon.kernel.support.services;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.TypedQuery;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import cn.bonoon.kernel.query.Pageable;
import cn.bonoon.kernel.support.ProxyEntityManager;
import cn.bonoon.kernel.support.entities.Persistable;
import cn.bonoon.kernel.support.models.Page;
import cn.bonoon.kernel.support.searcher.ISearcher;
import cn.bonoon.kernel.support.searcher.StandardSearcher;

/**
 * 比较底层的一个服务接口
 * @author jackson
 *
 * @param <E>
 */
public abstract class ServiceSupport implements ApplicationContextAware, InitializingBean, ProxyEntityManager{

	private static final Map<Class<?>, ISearcher> searcherMapped = new ConcurrentHashMap<>();
	
	/**
	 * 这个接口应该是可以被替换掉的
	 */
	protected EntityManager entityManager;
	
	protected ApplicationContext applicationContext;
	
	protected ServiceSupport() { }
	
	@Override
	public void afterPropertiesSet() throws Exception { }
	
	/**
	 * 根据类型<code>entityClass</code>进行{@link ISearcher}的注册
	 * @param applicationContext
	 * @param entityClass
	 * @return
	 */
	protected ISearcher _register(Class<? extends Persistable> entityClass){
		ISearcher searcher = searcherMapped.get(entityClass);
		if(null == searcher){
			searcher = new StandardSearcher(entityClass, applicationContext);
			searcherMapped.put(entityClass, searcher);
		}
		return searcher;
	}
	
	@Override
	public final void setApplicationContext(ApplicationContext context) throws BeansException {
		applicationContext = context;
	}
	
	@Override
	public <I> List<I> resultList(Class<I> iClass, String ql, Iterable<Object> parameters) {
		TypedQuery<I> query = entityManager.createQuery(ql, iClass);
		__query(query, parameters);
		return query.getResultList();
	}
	
	@Override
	@SuppressWarnings("unchecked")
	public List<Object> resultList(String ql, Iterable<Object> parameters) {
		Query query = entityManager.createQuery(ql);
		__query(query, parameters);
		return query.getResultList();
	}
	
	@Override
	@SuppressWarnings("unchecked")
	public Page resultPage(String sql, String cql, Pageable pageable, Iterable<Object> parameters) {
		Query query = entityManager.createQuery(sql), count = entityManager.createQuery(cql);
		int i = 1;
		for(Object par : parameters){
			count.setParameter(i, par);
			query.setParameter(i++, par);
		}
		query.setFirstResult(pageable.getFirstItem());
		query.setMaxResults(pageable.getPageSize());
		return new Page(((Number)count.getSingleResult()).longValue(), query.getResultList());
	}
	
	protected void __query(Query query, Iterable<Object> parameters){
		int i = 1;
		for(Object par : parameters){
			query.setParameter(i++, par);
		}
	}

	public final ApplicationContext getApplicationContext() {
		return applicationContext;
	}
	
	@PersistenceContext
	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}
	
	protected <T> TypedQuery<T> __query(Class<T> clazz, String hql) {
		return entityManager.createQuery(hql, clazz);
	}

	protected <T> TypedQuery<T> __query(Class<T> clazz, String hql, Object arg) {
		TypedQuery<T> tqSelect = entityManager.createQuery(hql, clazz);
		return tqSelect.setParameter(1, arg);
	}

	protected <T> TypedQuery<T> __query(Class<T> clazz, String hql, Object arg, Object arg2) {
		TypedQuery<T> tqSelect = entityManager.createQuery(hql, clazz);
		return tqSelect.setParameter(1, arg).setParameter(2, arg2);
	}

	protected <T> TypedQuery<T> __query(Class<T> clazz, String hql, Object... args) {
		TypedQuery<T> tqSelect = entityManager.createQuery(hql, clazz);
		for (int i = 0, l = args.length; i < l; i++) {
			tqSelect.setParameter(i + 1, args[i]);
		}
		return tqSelect;
	}

	//---------------------------------exsit
	protected boolean __exsit(String ql){
		return __exsit(__query(Number.class, ql));
	}
	
	protected boolean __exsit(String ql, Object arg){
		return __exsit(__query(Number.class, ql, arg));
	}
	
	protected boolean __exsit(String ql, Object arg, Object arg2){
		return __exsit(__query(Number.class, ql, arg, arg2));
	}
	
	protected boolean __exsit(String ql, Object... args){
		return __exsit(__query(Number.class, ql, args));
	}
	
	protected boolean __exsit(TypedQuery<Number> t){
		return t.getSingleResult().intValue() > 0;
	}

	//---------------------------------first
	protected <T> T __first(TypedQuery<T> tqSelect) {
		List<T> items = tqSelect.setMaxResults(1).getResultList();
		return items.isEmpty() ? null : items.get(0);
	}

	protected <T> T __first(Class<T> clazz, String hql) {
		return __first(__query(clazz, hql));
	}

	protected <T> T __first(Class<T> clazz, String hql, Object arg) {
		return __first(__query(clazz, hql, arg));
	}

	protected <T> T __first(Class<T> clazz, String hql, Object arg, Object arg2) {
		return __first(__query(clazz, hql, arg, arg2));
	}

	protected <T> T __first(Class<T> clazz, String hql, Object... args) {
		return __first(__query(clazz, hql, args));
	}

	//---------------------------------single
	protected <T> T __single(Class<T> clazz, String hql) {
		return __query(clazz, hql).getSingleResult();
	}

	protected <T> T __single(Class<T> clazz, String hql, Object arg) {
		return __query(clazz, hql, arg).getSingleResult();
	}

	protected <T> T __single(Class<T> clazz, String hql, Object arg, Object arg2) {
		return __query(clazz, hql, arg, arg2).getSingleResult();
	}

	protected <T> T __single(Class<T> clazz, String hql, Object... args) {
		return __query(clazz, hql, args).getSingleResult();
	}

	//---------------------------------list
	protected <T> List<T> __list(Class<T> clazz, String hql) {
		return __query(clazz, hql).getResultList();
	}

	protected <T> List<T> __list(Class<T> clazz, String hql, Object arg) {
		return __query(clazz, hql, arg).getResultList();
	}

	protected <T> List<T> __list(Class<T> clazz, String hql, Object arg, Object arg2) {
		return __query(clazz, hql, arg, arg2).getResultList();
	}
	
	protected <T> List<T> __list(Class<T> clazz, String hql, Object... args) {
		return __query(clazz, hql, args).getResultList();
	}
	
//---------------------------------top
	protected <T> List<T> __top(int top, Class<T> clazz, String hql) {
		return __query(clazz, hql).setMaxResults(top).getResultList();
	}

	protected <T> List<T> __top(int top, Class<T> clazz, String hql, Object arg) {
		return __query(clazz, hql, arg).setMaxResults(top).getResultList();
	}

	protected <T> List<T> __top(int top, Class<T> clazz, String hql, Object arg, Object arg2) {
		return __query(clazz, hql, arg, arg2).setMaxResults(top).getResultList();
	}
	
	protected <T> List<T> __top(int top, Class<T> clazz, String hql, Object... args) {
		return __query(clazz, hql, args).setMaxResults(top).getResultList();
	}

//	//---------------------------------page
//	protected <T> List<T> __page(int skip, int size, Class<T> clazz, String hql) {
//		return __query(clazz, hql).setFirstResult(skip).setMaxResults(size).getResultList();
//	}
//
//	protected <T> List<T> __page(int skip, int size, Class<T> clazz, String hql, Object arg) {
//		return __query(clazz, hql, arg).setFirstResult(skip).setMaxResults(size).getResultList();
//	}
//
//	protected <T> List<T> __page(int skip, int size, Class<T> clazz, String hql, Object arg, Object arg2) {
//		return __query(clazz, hql, arg, arg2).setFirstResult(skip).setMaxResults(size).getResultList();
//	}
//	
//	protected <T> List<T> __page(int skip, int size, Class<T> clazz, String hql, Object... args) {
//		return __query(clazz, hql, args).setFirstResult(skip).setMaxResults(size).getResultList();
//	}

	//---------------------------------exec
	protected int __exec(String ql){
		return entityManager.createQuery(ql).executeUpdate();
	}

	protected int __exec(String ql, Object arg) {
		return entityManager.createQuery(ql).setParameter(1, arg).executeUpdate();
	}

	protected int __exec(String ql, Object arg, Object arg2) {
		return entityManager.createQuery(ql).setParameter(1, arg).setParameter(2, arg2).executeUpdate();
	}

	protected int __exec(String ql, Object arg, Object arg2, Object arg3) {
		return entityManager.createQuery(ql)
				.setParameter(1, arg)
				.setParameter(2, arg2)
				.setParameter(3, arg3).executeUpdate();
	}
}
